package redisCluster

import (
	xlog "Hypnos-Soundcore/components/log"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

const (
	TagRedisCluster           = "redisCluster"
	ResultYes                 = 1
	ActionGetObject           = "GetObject"
	ActionSetObjectWithExpire = "SetObjectWithExpire"
	ActionExpire              = "Expire"
	ActionDelete              = "Delete"
	ActionExists              = "Exists"
	ActionHMSET               = "HMSET"
	ActionHMGET               = "HMGET"
	ActionHDEL                = "HDEL"
	ActionHmDelete            = "HmDelete"
	ActionHincrby             = "Hincrby"
	ActionHmgetKeysValues     = "HmgetKeysValues"
	ActionDeleteKeys          = "DeleteKeys"
	ActionSetStringWithExpire = "SetStringWithExpire"
	ActionHGetObject          = "HGetObject"
	ActionHSetObject          = "HSetObject"
	ActionHExists             = "HExists"
	ActionGetString           = "GetString"
	ActionSetObject           = "SetObject"
	ActionSetNX               = "SetNX"
	ActionIncr                = "Incr"
	ActionSetString           = "SetString"
	ActionPing                = "Ping"
)

var (
	Debug                      = true // 打印日志
	ErrorUnknown               = errors.New("unknown error")
	ErrorNotPointerToStruct    = errors.New("value must be non-nil pointer to a struct")
	ErrorProvidedValueNotMatch = errors.New("provided value type didn't match obj field type")
	ErrInputNotSlicePtr        = errors.New("Input must be slice ptr type")
	ErrKeyIsBlank              = errors.New("redis: key should not be blank")
)

// ObjField .
type ObjField struct {
	NameOrg   string
	NameSpec  string
	Omitempty bool
	Nameempty bool
}

// GetName .
func (of *ObjField) GetName() string {
	if of.Nameempty {
		return ""
	}
	if len(of.NameSpec) > 0 {
		return of.NameSpec
	}
	return of.NameOrg
}

func parseField(field reflect.StructField) (ret ObjField) {
	tag := field.Tag
	ret.NameOrg = field.Name
	// redis:"name,omitempty"
	rtag := tag.Get("redis")
	rarr := strings.Split(rtag, ",")
	for _, value := range rarr {
		if value == "-" {
			ret.Nameempty = true
		} else if value == "omitempty" {
			ret.Omitempty = true
		} else if len(value) > 0 {
			ret.NameSpec = value
		}
	}
	return ret
}

func flattenStruct(in interface{}) (ret map[string]interface{}, err error) {
	out := make(map[string]interface{})

	v := reflect.ValueOf(in)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	// we only accept structs
	if v.Kind() != reflect.Struct {
		return nil, fmt.Errorf("flattenStruct only accepts structs; got %T", v)
	}

	typ := v.Type()
	for i := 0; i < v.NumField(); i++ {
		if v.Field(i).Type().Kind() == reflect.Struct {
			var substrcut map[string]interface{}
			if substrcut, err = flattenStruct(v.Field(i).Interface()); err != nil {
				return
			}
			for k, v := range substrcut {
				out[k] = v
			}
			continue
		}

		objField := parseField(typ.Field(i))
		name := objField.GetName()
		if len(name) == 0 {
			continue
		}
		if objField.Omitempty && v.Field(i).Type().Kind() == reflect.String {
			if val := v.Field(i).Interface().(string); len(val) == 0 {
				continue
			}
		}
		out[name] = v.Field(i).Interface()
	}
	return out, nil
}

func fillStruct(m map[string]string, s interface{}) error {
	if len(m) == 0 {
		return nil
	}
	structType := reflect.TypeOf(s)
	structValue := reflect.ValueOf(s)

	if structValue.Kind() != reflect.Ptr || structValue.IsNil() {
		return ErrorNotPointerToStruct
	}
	structType = structType.Elem()
	structValue = structValue.Elem()
	if structValue.Kind() != reflect.Struct {
		return ErrorNotPointerToStruct
	}

	for i := 0; i < structValue.NumField(); i++ {
		structFieldValue := structValue.Field(i)

		if structFieldValue.Type().Kind() == reflect.Struct {
			err := fillStruct(m, structFieldValue.Addr().Interface())
			if err != nil {
				return err
			}
			continue
		}
		objField := parseField(structType.Field(i))
		name := objField.GetName()
		value, ok := m[name]
		if !ok || len(value) == 0 {
			continue
		}

		if !structFieldValue.IsValid() {
			return fmt.Errorf("no such field: %s in obj", name)
		}

		if !structFieldValue.CanSet() {
			return fmt.Errorf("cannot set %s field value", name)
		}

		switch structFieldValue.Type().Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
			intval, _ := strconv.Atoi(value)
			structFieldValue.Set(reflect.ValueOf(intval))
		case reflect.Int64:
			int64val, _ := strconv.ParseInt(value, 10, 64)
			structFieldValue.Set(reflect.ValueOf(int64val))
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
			uintval, _ := strconv.ParseUint(value, 10, 32)
			structFieldValue.Set(reflect.ValueOf(uint(uintval)))
		case reflect.Uint64:
			uint64val, _ := strconv.ParseUint(value, 10, 64)
			structFieldValue.Set(reflect.ValueOf(uint64val))
		case reflect.Bool:
			boolval, _ := strconv.ParseBool(value)
			structFieldValue.Set(reflect.ValueOf(boolval))
		case reflect.String:
			structFieldValue.Set(reflect.ValueOf(value))
		case reflect.Float32:
			float64val, _ := strconv.ParseFloat(value, 32)
			structFieldValue.Set(reflect.ValueOf(float32(float64val)))
		case reflect.Float64:
			float64val, _ := strconv.ParseFloat(value, 64)
			structFieldValue.Set(reflect.ValueOf(float64val))
		default:
			return ErrorProvidedValueNotMatch
		}
	}
	return nil
}

func errPrintf(action string, err interface{}, format string, a ...interface{}) {
	if err != nil {
		errMsg := fmt.Sprintf("%v", err)
		xlog.ErrorActionPrintln(TagRedisCluster, action, "", errMsg, 0, fmt.Sprintf(format, a...))
		return
	}
	if !Debug {
		return
	}
	xlog.DebugActionPrintln(TagRedisCluster, action, "", "", 0, fmt.Sprintf(format, a...))
}
